/*MyPhotoIndex Free Photo Organizer <www.myphotoindex.com>
  Copyright (C) <2006-2009>  <Asaf Yarkoni asaf@yarkoni.net>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Threading;
using System.IO;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using MyPhotoIndex.Interfaces;
using MyPhotoIndex.Utilities;
using MyPhotoIndex.Interfaces.Imaging;

namespace MyPhotoIndex.Controls
{
    [Serializable]
    public class ImageInstance : IImageInstance
    {
        public event EventHandler<ImageDataChangedArgs> ImageDataChangedEvent;
        public event EventHandler<EventArgs> ImageDrawingRectCahngedEvent;
        public event EventHandler<EventArgs> DisposingEvent;

        private Rectangle       m_frameDrawingRect;
        private Rectangle       m_imageDrawingRect;
        private Rectangle       m_lastImageDrawingRect;
        private Size            m_imageSize;
        private int             m_imageId;
        private Image           m_image;
        private bool            m_imageLoaded;
        private bool            m_disposed;
        [NonSerializedAttribute]
        private IImageData      m_imageData;

        private EventHandler m_imageChangedEventHandler;

        public ImageInstance(IImageData imageData, Rectangle imageRect, Rectangle frameRect)
        {
            if (imageData == null)
            {
                return;
            }

            m_imageData = imageData;
            m_imageData.ThumbnailReferences++;
            if (imageData != null)
            {
                m_imageId = m_imageData.Id;

                m_imageChangedEventHandler = new EventHandler(m_imageData_ImageDataChangedEvent);
                imageData.ImageDataChangedEvent += m_imageChangedEventHandler;
            }

            m_frameDrawingRect = frameRect;
            m_imageDrawingRect = imageRect;
            m_lastImageDrawingRect = m_imageDrawingRect;

            if (m_image != null)
            {
                m_imageSize = new Size(m_image.Width, m_image.Height);
            }
        }

        void m_imageData_ImageDataChangedEvent(object sender, EventArgs e)
        {
            IImageData imageDataSender = (IImageData)sender;
            if (ImageDataChangedEvent != null && 
                imageDataSender.Id == m_imageId)
            {
                if (m_imageLoaded == true)
                {
                    m_imageLoaded = false;
                    m_image.Dispose();
                    m_image = null;
                }

                ImageDataChangedEvent.Invoke(this, new ImageDataChangedArgs(m_imageId));
            }
        }

        internal bool LoadImageFromDisk(bool force)
        {
            lock (m_imageData.SyncLock)
            {
                if (m_image == null &&
                    m_imageData.Thumbnail != null &&
                    (m_imageDrawingRect.Width <= m_imageData.Thumbnail.Width &&
                     m_imageDrawingRect.Height <= m_imageData.Thumbnail.Height) &&
                    force == false)
                {
                    if (ImageAlgo.IsPortraitImage(m_imageData.ImageSize) ==
                        ImageAlgo.IsPortraitImage(m_imageDrawingRect.Size))
                    {
                        return false;
                    }
                }
            }


            if ((m_imageDrawingRect.Width <= ImageSize.Width &&
                 m_imageDrawingRect.Height <= ImageSize.Height) &&
                force == false)
            {
                return false;
            }

            if (ImageSize.Width == ImageData.ImageSize.Width &&
                ImageSize.Height == ImageData.ImageSize.Height)
            {
                return false;
            }

            m_imageData.LoadImage(new Size(m_imageDrawingRect.Width, m_imageDrawingRect.Height), new ImageLoadDelegate(ImageLoaded));

            return true;
        }

        public void ImageLoaded(IImageData imageData, Image image, object tag)
        {
            if (imageData != null)
            {
                if (imageData.Id == m_imageData.Id &&
                    m_disposed == false)
                {
                    this.Image = image;
                }
            }
        }

        public Image Image
        {
            get 
            {
                if (m_image != null)
                {
                    return m_image;
                }

                return m_imageData.Thumbnail;
            }

            set 
            {
                if (value != null)
                {
                    if (m_image != null)
                    {
                        m_image.Dispose();
                        m_image = null;
                    }

                    m_image = value;
                    m_imageSize = new Size(m_image.Width, m_image.Height);
                    m_imageLoaded = true;
                    if (ImageDataChangedEvent != null)
                    {
                        ImageDataChangedEvent.Invoke(this, new ImageDataChangedArgs(m_imageId));
                    }
                }
            }
        }

        public IImageData ImageData
        {
            get { return m_imageData; }
        }

        public bool Disposed
        {
            get { return m_disposed; }
            set { m_disposed = value; }
        }

        public Rectangle FrameDrawingRect
        {
            get { return m_frameDrawingRect; }
            set { m_frameDrawingRect = value; }
        }

        public Rectangle ImageDrawingRect
        {
            get { return m_imageDrawingRect; }
            set 
            { 
                m_imageDrawingRect = value;
                if (m_lastImageDrawingRect.Equals(m_imageDrawingRect) == false)
                {
                    m_lastImageDrawingRect = m_imageDrawingRect;

                    if (ImageDrawingRectCahngedEvent != null)
                    {
                        ImageDrawingRectCahngedEvent.Invoke(this, EventArgs.Empty);
                    }
                }
            }
        }

        public bool ValidateThumbnailSize()
        {
            return !LoadImageFromDisk(false);
        }

        public Size ImageSize
        {
            get 
            {
                if (m_image != null)
                {
                    return m_imageSize;
                }

                return m_imageData.ThumbnailSize;
            }
            set { m_imageSize = value; }
        }

        public int ImageId
        {
            get { return m_imageId; }
            set { m_imageId = value; }
        }

        #region IDisposable Members

        ~ImageInstance()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (DisposingEvent != null)
            {
                DisposingEvent.Invoke(this, EventArgs.Empty);
            }

            if (disposing)
            {
                m_imageData.ThumbnailReferences--;
                if (m_imageLoaded)
                {
                    m_image.Dispose();
                }

                m_imageData.ImageDataChangedEvent -= m_imageChangedEventHandler;
            }

            m_disposed = true;
        }

        #endregion

        public void Rotate(RotateFlipType rotationType)
        {
            m_imageData.Rotate(rotationType);
        }
    }
}
